#!/usr/bin/perl -w

package MediaWiki::API;
use strict;
use XML::Simple;
use HTTP::Cookies;
use LWP::UserAgent;
use HTTP::Request::Common;
use Data::Dumper;
use Scalar::Util qw(blessed);
use Term::ANSIColor;

#All MediaWiki Packages
use MediaWiki::Object;
use MediaWiki::Page;
use MediaWiki::Config;

# initialize http and xml
my $xml = new XML::Simple;
our @ActiveSessions;
our $ActiveSession = undef;
sub new {
	my($type,$api,$verbose,$error) = @_;
	my $self = {
		username => undef,
		api => $api,
		verbose => $verbose,
		error => $error,
		ua => new LWP::UserAgent,
		editToken => 0,
		isLoggedIn => 0,
	};
	my $cookie_jar = new HTTP::Cookies;
	$self->{ua}->cookie_jar($cookie_jar);
	bless($self, $type);
	push @ActiveSessions, $self;
	$ActiveSession = $self;
	return $self;
}

sub default {
	my $self = MediaWiki::API->new($MediaWiki::Config::API,$MediaWiki::Config::Verbose,$MediaWiki::Config::Error);
	if ( $MediaWiki::Config::Login ) {
		$self->login($MediaWiki::Config::User,$MediaWiki::Config::Password);
	}
	return $self;
}

sub silent {
	my $self = MediaWiki::API->new($MediaWiki::Config::API,0,$MediaWiki::Config::Error);
	if ( $MediaWiki::Config::Login ) {
		$self->login($MediaWiki::Config::User,$MediaWiki::Config::Password);
	}
	return $self;
}

sub anon {
	my $self = MediaWiki::API->new($MediaWiki::Config::API);
	return $self;
}

sub login {
	my ($self,$username,$password,$token) = @_;
	my ($request);
	if ( $token ) {
		$request = {action=>'login',
		lgname=>$username,
		lgpassword=>$password,
		lgtoken=>$token,
		format=>'xml'};
	} else {
		$request = {action=>'login',
		lgname=>$username,
		lgpassword=>$password,
		format=>'xml'};
	}
	$self->verbose("Attempting to login as $username");	
	my $data = $self->xmlRequest($request);
	if ( $data ) {
		# Check if page successfully loaded
		if ( $data->{login}->{result} eq "NeedToken" ) {
			# MediaWiki Challenge for Authentication Token
			my $token = $data->{login}->{token};#Obtain challenge token... try again
			$self->verbose("Obtained authentication token: $token");
			return $self->login($username,$password,$token);
		} elsif ( $data->{login}->{result} eq "Success" ) {
			$self->verbose("Logged in successfully.");
			$self->{isLoggedIn} = 1;
			$self->{username} = $username;
			return 1;
		} else {
			$self->error("Login failed: " . $data->{login}->{result});
			return 0;
		}
	}
}

sub logout {
	my ($self) = @_;
	my $request = {action=>'logout',
	format=>'xml'};
	$self->xmlRequest($request);
	return 1;
}

sub xmlRequest {
	my ($self, $query) = @_;
	my $request = POST $self->{api}, $query;
	my $page = $self->{ua}->request($request);
	if ( $page->is_success ) {
		my $data = $xml->XMLin(trim($page->content));
		return $data;
	} else { 
		$self->error("Error when connecting to $self->{api}: ".$page->status_line);
		return 0;
	}
}

sub getEditToken {
	my ($self) = @_;
	if ( $self->{editToken} ) {
		return $self->{editToken};
	}
	my $query = {action=>'query',
	prop=>'info',
	intoken=>'edit',
	titles=>'Main Page',
	format=>'xml'};
	$self->verbose("Attempting to retrieve edit token...");
	my $data = $self->xmlRequest($query);
	if ( $data ) {
		if ( $data->{query}->{pages}->{page}->{edittoken} ) {
			$self->{editToken} = $data->{query}->{pages}->{page}->{edittoken};
			$self->verbose("Got edit token: $self->{editToken}");
			return $self->{editToken};
		} else {
			$self->error("Could not get edit token. $data->{warnings}->{info}->{content}");
			return 0;
		}
	}
}

sub writePage {
	my ($self, $pages) = @_;
	if ( !(blessed($pages) eq "MediaWiki::Page") ) {
		print "writePage takes in a MediaWiki::Page object\n";
		return 0;
	}
	my $hash = $pages->getQuery();
	$hash->{action} = 'edit';
	$hash->{token} = $self->getEditToken();
	my $data = $self->xmlRequest($hash);
	if ( $data ) {
		if ( $data->{edit}->{result} eq "Success" ) {
			$self->verbose("Successfully wrote page $data->{edit}->{title}",1);
			if ( $data->{edit}->{newrevid} ) {
				$self->verbose(" with new revision id $data->{edit}->{newrevid}.");
			} else {
				$self->verbose(".");
			}
			return 1;
		} else {
			$self->error("Error $data->{error}->{code}: $data->{error}->{info}");
			return 0;
		}
	}
}

sub getPage {
	my ($self, $title) = @_;
	my $request = {action => 'query',
	prop => 'revisions',
	rvprop => 'content',
	titles => $title,
	format => 'xml'};
	my $data = $self->xmlRequest($request);
	if ( $data ) {
		if ( defined($data->{query}->{pages}->{page}->{missing}) || defined($data->{query}->{pages}->{page}->{invalid}) ) {
			$self->error("The page you searched for is either missing or invalid.");
			return 0;
		} else {
			my $page = MediaWiki::Page->new($data->{query}->{pages}->{page}->{title});
			$page->{text} = $data->{query}->{pages}->{page}->{revisions}->{rev}->{content};
			$page->{pageid} = $data->{query}->{pages}->{page}->{pageid};
			$page->{ns} = $data->{query}->{pages}->{page}->{ns};
			return $page;
		}
	}
}

sub siteInfo {
	my ($self) = @_;
	my $request = {action => 'query',
	meta => 'siteinfo',
	format => 'xml',};
	my $data = $self->xmlRequest($request);
	if ( $data ) {
		if ( defined($data->{error}) ) {
			$self->error("Error $data->{error}->{code}: $data->{error}->{info}");
		} else {
			my $object = MediaWiki::Object->new();
			$object->{query} = $data->{query};
			return $object;
		}
	}
}
sub verbose {
	my ($self,$output,$noline) = @_;
	if ( $self->{verbose} && !$noline) {
		print $output . "\n";
	} elsif ( $self->{verbose} && $noline ) {
		print $output;
	}
}

sub error {
	my ($self,$output,$noline) = @_;
	if ( $self->{error} && !$noline) {
		print colored($output."\n", 'red');
	} elsif ( $self->{error} && $noline ) {
		print colored($output, 'red');
	}
}

# Internal Functions

sub getActiveSessions {
	return @ActiveSessions;
}

sub trim {
	my $string = shift;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}
1;
